import { ReactiveCommand } from './../libs/rxjs/cc3/ReactiveCommand';
import { udm } from './UserDataManager';
import { ReactiveProperty } from './../libs/rxjs/cc3/ReactiveProperty';
import { BaseManager } from './../libs/rxjs/cc3/BaseManager';
import { _decorator, Node, color, director, System, AudioSource, AudioClip, Prefab, instantiate, AudioSourceComponent } from 'cc';
import { Observable, Subscription, forkJoin, map } from 'rxjs';
import { bundleLoad, bundleLoadDir } from '../libs/rxjs/cc3/AssetManagerLoad';
import { MergeWithTimer } from '../libs/rxjs/cc3/Merge';
import { sys } from 'cc';
import { bdm } from '../SDK/ttScripts/ByteDanceManager';
import { wcm } from '../SDK/wxScripts/WechatManager';
import { nullifySubscription } from '../libs/rxjs/utils';
import { TransparentPass } from '../libs/rxjs/cc3/CommonObservable';
import { rm } from './ResourceManager';
const { ccclass, property } = _decorator;

// export enum BGM {
//     Menu,
//     Game
// }

@ccclass('AudioManager')
export class AudioManager extends BaseManager {

    public static instance: AudioManager;
    static ID = 'audio_system';
    get logColorOverride() { return color().fromHEX('#EECCDD') };

    static registerSystem() {
        director.unregisterSystem(director.getSystem(AudioManager.ID));
        director.registerSystem(AudioManager.ID, am, System.Priority.MEDIUM);
        am.init();
    }

    onInitManager() {
        this.log('onInitManager', this.timestamp);
    }

    // assetbundleName = 'ab_audio';

    bgm = ReactiveProperty.Create<string>(null);
    play = ReactiveCommand.Create<string>();
    stop = ReactiveCommand.Create<string>();

    vibrate = ReactiveCommand.Create<{ type: string }>();

    audioSource: AudioSource = null;
    audioSourceClipName: string = null;
    sfxAudioSource: Record<string, AudioSource> = {};

    _lastPlayStr: string = null;

    useObserves() {
        return [

            MergeWithTimer([
                udm.bgmOn._subject,
                this.bgm._subject
            ]).subscribe(_ => {
                if (udm.bgmOn.value) {
                    this.playBgm();
                } else {
                    this.stopBgm();
                }
            }),

            this.play.subscribe(_ => {
                if (udm.sfxOn.value) {
                    this.playSfx({ clipName: _ });
                }
            }),

            this.stop.subscribe(_ => {
                this.stopSfx({ clipName: _ });
            }),

            this.vibrate.subscribe(_ => {
                if (udm.vibrateOn.value) {
                    if (sys.platform == sys.Platform.BYTEDANCE_MINI_GAME) {
                        if (_?.type == 'long') {
                            bdm.vibrateLong();
                        } else {
                            bdm.vibrateShort();
                        }
                    } else if (sys.platform == sys.Platform.WECHAT_GAME) {
                        if (_?.type == 'long') {
                            wcm.vibrateLong();
                        } else {
                            wcm.vibrateShort();
                        }
                    }
                }
            }),

            rm.releaseAudioClipCommand.subscribe(_ => {
                if (!this.sfxAudioSource[_]) {
                    this.sfxAudioSource[_].destroy();
                }
                delete this.sfxAudioSource[_];
            }),
        ];
    }

    _sub: Record<string, Subscription> = {};

    genRootNode(assetbundleName: string) {
        nullifySubscription.call(this._sub, assetbundleName);
        return new Observable(observer => {
            let observables: Observable<any>[] = [];
            if (this.audioSource == null) {
                let observable = bundleLoad(assetbundleName, 'Audio/AudioSource', Prefab, _ => observer.next({ progress: _ })).pipe(map(_ => {
                    const { err, data } = _;
                    if (err != null) {
                        console.warn('load audioSource prefab err', err);
                        return;
                    }
                    const node = instantiate<Node>(data as any);
                    director.addPersistRootNode(node);
                    this.audioSource = node.getComponent(AudioSource);
                }));
                observables.push(observable);
            }

            this._sub[assetbundleName] = forkJoin(observables).subscribe(TransparentPass(observer));
            return () => {
                nullifySubscription.call(this._sub, assetbundleName);
            }
        });
    }

    playBgm() {
        this.log('playBgm', this.audioSource, this.bgm.value);
        if (this.audioSource != null) {
            let clipName = this.bgm.value;
            if (this.audioSourceClipName != clipName || !this.audioSource.playing) { // 避免重复播放相同音乐
                if (this.audioSource.playing) {
                    this.audioSource.stop();
                }
                this.audioSource.clip = rm.audioClips[clipName];
                this.audioSource.play();
                this.audioSourceClipName = clipName;
            }
            this.log('play');
        }
    }

    stopBgm() {
        if (this.audioSource != null) {
            this.audioSource.stop();
            this.log('stop');
        }
    }

    pauseBgm() {
        if (this.audioSource != null) {
            if (this.audioSource.playing) {
                this.audioSource.pause();
                this.log('pause');
            }
        }
    }

    playSfx(param: { clipName: string }) {
        this.log('playSfx', param.clipName);
        const { clipName } = param;
        if (this.sfxAudioSource == null) return;
        let audioSource = this.sfxAudioSource[clipName];
        if (audioSource == null) {
            audioSource = this.audioSource.node.addComponent(AudioSource);
            audioSource.clip = rm.audioClips[clipName];
            this.sfxAudioSource[clipName] = audioSource;
        }
        audioSource.play();
    }

    stopSfx(param: { clipName: string }) {
        this.log('playSfx', param.clipName);
        const { clipName } = param;
        if (this.sfxAudioSource == null) return;
        let audioSource = this.sfxAudioSource[clipName];
        if (audioSource != null) {
            if (audioSource.playing) {
                audioSource.stop();
            }
        }
    }
}
globalThis.AudioManager = AudioManager;

export const am = AudioManager.instance = new AudioManager();